package com.kot.backend.consumer;

import com.alibaba.fastjson2.JSONObject;
import com.kot.backend.consumer.utils.Game;
import com.kot.backend.consumer.utils.JwtAuthentication;
import com.kot.backend.mapper.UserMapper;
import com.kot.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint("/websocket/{token}")  // 表明映射到前端哪个链接，这里也是用jwt验证身份，注意不要以'/'结尾
public class WebSocketServer {

    //与线程安全有关的哈希表（每个实例在线程里面，所以要线程安全），将userID映射到相应用户的WebSocketServer
    // 需要一个对所有websocket可见的全局变量(static实现，和对象无关，绑定在类上)，存储所有的链接
    private static ConcurrentHashMap<Integer, WebSocketServer> users = new ConcurrentHashMap<>();

    final private static CopyOnWriteArraySet<User> matchpool = new CopyOnWriteArraySet<>();//用线程安全的set定义安全的匹配池
    //当前链接请求的用户
    private User user;
    private Game game = new Game();
    private User opponent;//记录对手
    private Session session = null;//不同于http协议中的session，每个链接都是用session来维护


    //因为websocket不是spring组件，所以不能直接单例注入，需要在setter注入静态变量
    private static UserMapper userMapper = null;//用类名来访问
    @Autowired
    public void setUserMapper(UserMapper userMapper){
        WebSocketServer.userMapper = userMapper;//用类名来访问
    }
    //注入在上面




    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) throws IOException {
        // 建立连接，触发此函数
//        System.out.println("connected!");
        this.session = session;
        //为了方便调试，初阶段只把token当成userId看
        Integer userId = JwtAuthentication.getUserId(token);//调用工具类获取UserId
        this.user = userMapper.selectById(userId);
        if (this.user != null) {
            users.put(userId, this);//用户存在，就是合法建立连接
        } else {
            this.session.close();//用户不合法，直接关闭连接
        }
//        System.out.println(users);//调试看看所有链接
    }

    @OnClose
    public void onClose() {
        // 关闭链接，触发此函数
        System.out.println("disconnected!");
        //断开连接的话要将user移除
        if (this.user != null) {
            users.remove((this.user.getId()));//从全局哈希表中删除该链接
        }
    }

    private void startMatching() {
        System.out.println("start mathcing!");
        matchpool.add(this.user);
        if (matchpool.size() >= 2) {
            Iterator<User> it = matchpool.iterator(); //枚举
            User a = it.next(), b = it.next();

            this.opponent = a.getId()==this.user.getId()?b:a;//先直接记录对手
            System.out.println(this.user+"的对手是"+this.opponent);

//            matchpool.remove(a);//匹配完后，从匹配池中删除
//            matchpool.remove(b);

            JSONObject respA = new JSONObject();//匹配成功后，传给A的信息
            respA.put("event", "start-matching");//匹配成功的提示信息
            respA.put("opponent_id", b.getId());
            respA.put("opponent_kind","playerA");//对手坦克的种类
            respA.put("opponent_username", b.getUsername());//传对手信息
            respA.put("opponent_photo", b.getPhoto());
            //用users获取a的链接
            users.get(a.getId()).sendMessage(respA.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）

            JSONObject respB = new JSONObject();//匹配成功后，传给B的信息
            respB.put("event", "start-matching");
            respB.put("opponent_id", a.getId());
            respB.put("opponent_kind","playerB");//对手坦克的种类
            respB.put("opponent_username", a.getUsername());
            respB.put("opponent_photo", a.getPhoto());
            //用users获取b的链接
            users.get(b.getId()).sendMessage(respB.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
        }
    }
    private void stopMatching() {
        System.out.println("stop matching!");
        matchpool.remove(this.user);
    }
    private void move(Integer direction) {//只会传入01234;4表示停止
        JSONObject resp = new JSONObject();//A操作完后，把操作传递给B
        System.out.println("首先，我收到方向信息了");
        if(direction<4) {
            resp.put("event", "move");//确定信息类型
            resp.put("direction", direction);
            resp.put("status","move");


            //现在没有opponent了，要等匹配系统写好（多线程维护），才能让每个玩家都能拿到对手



            System.out.println("**********"+this.opponent);
            if(this.opponent!=null){
                System.out.println("我信息也发出去了");
                users.get(this.opponent.getId()).sendMessage(resp.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
            }
        }else {
            resp.put("event", "stop");//确定信息类型
            resp.put("status","idle");
            if(this.opponent!=null)users.get(this.opponent.getId()).sendMessage(resp.toJSONString()); //用这个链接将信息传回给前端（在这里发送信息）
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {//当作路由，根据从前端传来的message判断之后执行哪一个函数
        // 从Client接收消息
//        System.out.println("receive message!");
        JSONObject data = JSONObject.parseObject(message);//字符串转换成json
        String event = data.getString("event");//从json中获取event，判断当前是在开始匹配，还是结束匹配
        if ("start-matching".equals(event)) {
            startMatching();//把开始匹配的业务封装成一个函数
        } else if ("stop-matching".equals(event)) {
            stopMatching();//把结束匹配的业务封装成一个函数
        }else if ("move".equals(event)) {//前端向后端发送信息，事件类型是move
            move(data.getInteger("direction"));//调用move(方向)
        }
    }




    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    public void sendMessage(String message){//自定义一个发送信息的函数（websocket都是异步通信过程，所以发信息要加锁）
        synchronized (this.session) { //加锁
            try {
                this.session.getBasicRemote().sendText(message); //从后端向当前链接发送信息
            } catch (IOException e) { //IO异常
                e.printStackTrace();
            }
        }
    }
}
